<template>
    <div ref="root" class="number-scroll">
        <template v-for="(char, idx) of displayChars" :key="idx">
            <!-- 数字 -->
            <div v-if="!isNaN(+char)" class="digit-wrapper" :key="`ul-${idx}`" :style="{ width: digitSize + 'px', height: digitSize + 'px' }">
                <ul class="digit-list" :style="getDigitListStyle(+char)">
                    <li v-for="n in 10" :key="n" class="digit">{{ n-1 }}</li>
                </ul>
            </div>
            <!-- 其它字符 -->
            <span v-else class="symbol" :style="{ fontSize: symbolSize + 'px' }">
                {{ char }}
            </span>
        </template>
    </div>
</template>

<script lang="ts" setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";

interface Props {
    value: number;
    duration?: number;
    intLen?: number;
    decimals?: number;
    digitRatio?: number; // 方块大小 = 父高度 * digitRatio，默认 1
}
const props = withDefaults(defineProps<Props>(), {
    duration: 1.5,
    intLen: 6,
    decimals: 0,
    digitRatio: 1,
});
const getDigitListStyle = computed(() => {
    return (char: string | number) => {
        let style = `transition:all ${props.duration}s linear;`;
         style += `transform:translateY(-${+char * 10}%);`;
        return style;
    };
});
const root = ref<HTMLElement>();
const digitSize = ref(60);
const symbolSize = computed(() => digitSize.value * 0.8);

/* ---------- 监听父元素高度，计算 digitSize ---------- */
let ro: ResizeObserver | null = null;
const updateSize = () => {
    if (!root.value) return;
    const h = root.value.getBoundingClientRect().height;
    digitSize.value = Math.max(24, h * props.digitRatio);
};

onMounted(() => {
    updateSize();
    ro = new ResizeObserver(updateSize);
    ro.observe(root.value!);
});

onBeforeUnmount(() => {
    ro?.disconnect();
});

/* ---------- 数字格式化 ---------- */
const displayChars = ref<string[]>([]);
const format = (val: number) => {
    const fixed = val.toFixed(props.decimals);
    return fixed.replace(/\B(?=(\d{3})+(?!\d))/g, ",").split("");
};
watch(
    () => props.value,
    val => {
        /* 1. 先格式化成带千分位的数组，例如 95665 -> ["9","5","6",",","6","5"] */
        const arr = format(val);
        /* 2. 统计“整数部分的纯数字”个数（小数点后面的不算）*/
        const intEndIdx = arr.indexOf("."); // 没小数点时为 -1
        const intDigits = arr.slice(0, intEndIdx === -1 ? arr.length : intEndIdx).filter(v => v !== ",").length;
        /* 3. 需要补的零个数 = 期望整数位数 - 实际整数位数 */
        const pad = props.intLen - intDigits;
        /* 4. 生成最终要渲染的字符数组 */
        displayChars.value =
            pad > 0
                ? "0".repeat(pad).split("").concat(arr) // 前面补 0
                : arr;
    },
    { immediate: true }
);
</script>

<style scoped>
.number-scroll {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
}

.digit-wrapper {
    position: relative;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.1);
    border-radius: 6px;
    font-family: "DIN Condensed", sans-serif;
    color: #00f2f1;
}

.digit-list {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 1000%; /* 10 个数字 */
}

.digit {
    height: 10%;
    display: flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
}

.symbol {
    font-family: inherit;
    color: #00f2f1;
    line-height: 1;
    letter-spacing: 2px;
}
</style>
